#include <cstdlib>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "projection.h"
#include "minIni.h"
#include "config.h"
#include "resources.h"
#include "map.h"
#include "spriteanimator.h"
#include "sprite.h"
#include "blitqueue.h"
#include "grcontext.h"
#include "entityaction.h"
#include "entity.h"
#include "entitymanager.h"
#include "stdcharanim.h"

using namespace Oribi;

Entity::Entity():
    gender   (rand() % 2),
    direction(DIRECTION_RIGHT),
    actions  (0),
    position({0, 0}),
    height   (0)
{
    setState(ENTITY_STATE_IDLE);
}

Entity::Entity(const Entity& rhs):
    state    (rhs.direction),
    direction(rhs.direction),
    gender   (rhs.gender),
    actions ((rhs.actions) ? rhs.actions->clone() : 0),
    position (rhs.position),
    height   (rhs.height),
    sprite   (rhs.sprite){}

AnimatedSprite& Entity::getSprite()
{
    return sprite;
}

SDL_Point Entity::getPosition() const
{
    return position;
}

void Entity::setPosition(SDL_Point p)
{
    position = p;
}

void Entity::blit(GraphicContext& gr)
{
    setSpritePosition();
    gr.addSpriteToBlitQueue(sprite);
}

void Entity::blit(BlitQueue& q)
{
    setSpritePosition();
    q.queueSprite(sprite);
}

void Entity::initSprite(Resources& res, ConfContext& conf)
{
    int sheetID = getSpriteSheetID();

    sprite.sheet = res.getSpriteSheet(sheetID);

    std::string confName = "res/char/";
    confName.append(getEntityTypeName());
    confName.append((gender == ENTITY_GENDER_MALE) ?
        "/male/conf.ini" : "/female/conf.ini");

    /* Does nothing if the file is already present */
    conf.addFile(confName);
    ConfigFile *f = conf.getFile(confName);
    if (f)
    {
        for (int i = 0; i < N_STD_CHAR_ANIM; ++i)
        {
            for (int d = 0; d < 8; ++d)
            {
                sprite.addAnimation(SpriteAnimation(StdCharAnim::getName(i, d), *f));
            }
        }

        sprite.rect.x = f->geti("Data", "rectX", 0);
        sprite.rect.y = f->geti("Data", "rectY", 0);

        sprite.rect.w = f->geti("Data", "rectW", 16);
        sprite.rect.h = f->geti("Data", "rectH", 32);

        sprite.base.x = f->geti("Data", "baseX", 12);
        sprite.base.y = f->geti("Data", "baseY", 24);
    }

    sprite.position = projectToIsometric(position.x, position.y, height);
    sprite.runAnimation(StdCharAnim::Idle(0));
}

void Entity::updateHeight(const Map& m)
{
    SDL_Point i = {position.x / MapTile::xSize, position.y / MapTile::ySize};
    height = m.getTileHeight(i.x, i.y);
}

void Entity::setSpritePosition()
{
    sprite.position = projectToIsometric(position.x, position.y, height);
}

void Entity::setGender(int g)
{
    if (g < 1)
        gender = ENTITY_GENDER_MALE;
    else
        gender = ENTITY_GENDER_FEMALE;
}

int Entity::getGender() const
{
    return gender;
}

int Entity::randomizeGender()
{
    gender = rand() % 2;
    return gender;
}

void Entity::updateAnimationToState()
{
    sprite.runAnimation(StdCharAnim::getAnim(animOfState(), direction));
}

void Entity::setState(int s)
{
    if (isValidState(s))
    {
        state = s;
        updateAnimationToState();
    }
}

void Entity::setDirection(int d)
{
    if (d > -1 && d < 8)
    {
        direction = d;
        updateAnimationToState();
    }
}

void Entity::updateActions()
{
    if (!actions)
        return;

    int ret = actions->actOnEntity(*this);

    EntityAction *act = actions;
    EntityAction *n = actions->next;

    switch (ret)
    {
    case ACTION_DESTROY:
        actions = n;
        delete act;
        break;

    case ACTION_MOVE_BACK:
        if (n)
        {   act->next = n;
            n->next = act;
        }
        break;

    case ACTION_MOVE_TO_LAST:
        if (n)
        {
            actions = n;

            while (n->next)
                n = n->next;

            n->next = act;
        }
        break;

    default:
        break;
    }
}

void Entity::update()
{
    updateActions();
    sprite.animate();
}

int Entity::animOfState()
{
    switch (state)
    {
    case ENTITY_STATE_WALK:
        return StdCharAnim::WalkID;
    case ENTITY_STATE_RUN:
        return StdCharAnim::RunID;
    default:
        return StdCharAnim::IdleID;
    }
}

bool Entity::isValidState(int s) const
{
    return (s == ENTITY_STATE_IDLE
         || s == ENTITY_STATE_WALK
         || s == ENTITY_STATE_RUN);
}
